<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <input type="text" v-model='text'>
        <!-- <p id="b"></p> -->
        <p>{{text}}</p>
    </div>
    <script>
        function Vue(options) {
            this.data = options.data
            var data = this.data
            var id = options.el
            observe(data, this)
            var dom = nodeToFragment(document.getElementById(id), this)
            document.getElementById(id).appendChild(dom)
        }

        function nodeToFragment(node, vm) {
            var fragment = document.createDocumentFragment()
            var child
            while (child = node.firstChild) {
                compile(child, vm)
                fragment.appendChild(child)
            }
            return fragment
        }

        function compile(node, vm) {
            var reg = /\{\{(.*)\}\}/
            // 如果是结点元素
            if (node.nodeType === 1) {
                var attrs = node.attributes
                for (let i = 0; i < attrs.length; i++) {
                    if (attrs[i].nodeName === 'v-model') {
                        var name = attrs[i].nodeValue
                        // node.value = vm.data[name]
                        node.addEventListener('input', function (e) {
                            vm[name] = e.target.value
                        })
                        node.value = vm[name]
                        node.removeAttribute('v-model')
                    }
                }
            }
            // 如果是文本结点
            if (node.nodeType === 3) {
                console.log(node);
                if (reg.test(node.nodeValue)) {
                    // 获取匹配到的第一个字符串
                    var name = RegExp.$1
                    name = name.trim()
                    // node.nodeValue = vm.data[name]
                    // node.nodeValue = vm[name]
                    new Watcher(vm,node,name)
                }
            }
        }

        // 监听数据的变化
        function defineReactive(obj, key, val) {
            var dep = new Dep()
            Object.defineProperty(obj, key, {
                get: function () {
                    if(Dep.target) {
                        dep.addSub(Dep.target)
                    }
                    return val
                },
                set: function (newVal) {
                    if (val === newVal) {
                        return
                    }
                    val = newVal
                    console.log('set:' + newVal);
                    // 一旦更新立马通知
                    dep.notify()
                }
            })
        }

        // 添加观察函数
        function observe(obj, vm) {
            for (let key of Object.keys(obj)) {
                defineReactive(vm, key, obj[key])
            }
        }

        // Watcher构造函数
        function Watcher(vm,node,name) {
            Dep.target = this //Dep.target是一个全局变量
            this.vm = vm
            this.node = node
            this.name = name
            this.update()
            Dep.target = null
        }
        Watcher.prototype = {
            update() {
                this.get()
                this.node.nodeValue = this.value
            },
            get() {
                this.value = this.vm[this.name]
            }
        }

        // Dep构造函数
        function Dep() {
            this.subs = []
        }
        Dep.prototype = {
            addSub(sub) {
                this.subs.push(sub)
            },
            notify() {
                this.subs.forEach(function(sub) {
                    sub.update()
                })
            }
        }

        var vm = new Vue({
            el: 'app',
            data: {
                text: 'hello'
            }
        })


        // var obj = {}
        // var val = 'hello'
        // Object.defineProperty(obj, 'hello', {
        //     get: function(){
        //         console.log('get:'+val);
        //         return val
        //     },
        //     set:function(newVal) {
        //         val = newVal
        //         console.log('set:'+newVal);
        //         document.getElementById('a').value = val
        //         document.getElementById('b').innerText = val
        //     }
        // })
        // document.addEventListener('keyup', function(e){
        //     obj.hello = e.target.value
        // })
    </script>
</body>

</html>